﻿using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Threading.Tasks;

namespace StableDiffusionTools.Imaging
{
    public static class BitmapResizer
    {
        public static Bitmap ResizeBitmap(Bitmap image, BitmapResizeSettings settings)
        {
            Size preferredSize;

            if (!double.IsNaN(settings.ResizeScalar))
            {
                preferredSize = CalculatePrefferedSizeScalar(image.Size, settings.ResizeScalar, settings.MaintainAspectRatio, settings.AddPadding);
            }
            else
            {
                preferredSize = settings.ResizeSize;
            }

            Rectangle destinationRect = new Rectangle(0, 0, preferredSize.Width, preferredSize.Height);
            int canvasWidth = preferredSize.Width;
            int canvasHeight = preferredSize.Height;

            if (settings.MaintainAspectRatio)
            {
                CalculateImageAspectRatio(image.Size, preferredSize, settings.AddPadding, out Rectangle imageRect, out Size canvasSize);
                destinationRect = imageRect;
                canvasWidth = canvasSize.Width;
                canvasHeight = canvasSize.Height;
            }

            Bitmap outputImage = null;
            if (settings is BitmapResizeGDISettings gdi)
            {
                outputImage = new Bitmap(canvasWidth, canvasHeight);
                using (Graphics g = Graphics.FromImage(outputImage))
                {
                    if (settings.MaintainAspectRatio && settings.AddPadding)
                        g.Clear(settings.PaddingColor);

                    g.InterpolationMode = gdi.InterpolationMode;
                    g.SmoothingMode = gdi.SmoothingMode;
                    g.PixelOffsetMode = gdi.PixelOffsetMode;
                    g.CompositingQuality = gdi.CompositingQuality;
                    ImageAttributes attr = new ImageAttributes();
                    attr.SetWrapMode(WrapMode.TileFlipXY);
                    g.DrawImage(image, destinationRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attr);
                }
            }
            else if(settings is BitmapResizeLanczosSettings lanczos)
            {
                var cInput = cImage.FromBitmap(image);
                var sampler = new RadiusResampler(WindowType.Lanczos);
                var cOutput = sampler.Apply(cInput, canvasWidth, canvasHeight, lanczos.Radius, lanczos.UseCenteredGrid);
                outputImage = cOutput.ToBitmap();
            }

            return outputImage;
        }
        public static void CalculateImageAspectRatio(Size sourceSize, Size preferredSize, bool addPadding, out Rectangle imageRect, out Size canvasSize) =>
            CalculateImageAspectRatio(sourceSize, preferredSize.Width, preferredSize.Height, addPadding, out imageRect, out canvasSize);
        public static void CalculateImageAspectRatio(Size sourceSize, int preferredWidth, int preferredHeight, bool addPadding, out Rectangle imageRect, out Size canvasSize)
        {
            double ratioX = preferredWidth / (double)sourceSize.Width;
            double ratioY = preferredHeight / (double)sourceSize.Height;
            double ratio = ratioX < ratioY ? ratioX : ratioY;
            double newImageWidth = sourceSize.Width * ratio;
            double newImageHeight = sourceSize.Height * ratio;

            if (addPadding)
            {
                imageRect = new Rectangle((int)((preferredWidth - newImageWidth) / 2), (int)((preferredHeight - newImageHeight) / 2), (int)newImageWidth, (int)newImageHeight);
                canvasSize = new Size(preferredWidth, preferredHeight);
            }
            else
            {
                imageRect = new Rectangle(0, 0, (int)newImageWidth, (int)newImageHeight);
                canvasSize = new Size(imageRect.Width, imageRect.Height);
            }
        }

        public static Bitmap SupersampleResizeBitmap(Bitmap sourceBitmap, BitmapResizeSettings super, BitmapResizeSettings final)
        {
            Bitmap result;
            using (Bitmap intermediaryBitmap = ResizeBitmap(sourceBitmap, super))
            {
                result = ResizeBitmap(intermediaryBitmap, final);
            }
            return result;
        }

        public static async Task<Bitmap> SupersampleResizeBitmapAsync(Bitmap sourceBitmap, BitmapResizeSettings super, BitmapResizeSettings final)
        {
            Bitmap result = null;

            result = await Task.Run(() =>
            {
                using (Bitmap intermediaryBitmap = ResizeBitmap(sourceBitmap, super))
                {
                    return ResizeBitmap(intermediaryBitmap, final);
                }
            });

            return result;
        }

        public static Size CalculatePrefferedSizeScalar(Size source, double scalar, bool maintainAspectRatio, bool addPadding)
        {
            if (scalar == 0)
                throw new InvalidOperationException(nameof(scalar));

            int width = (int)(source.Width * scalar);
            int height = (int)(source.Height * scalar);

            if (width == 0)
                throw new InvalidOperationException(nameof(width));
            if (height == 0)
                throw new InvalidOperationException(nameof(height));

            int canvasWidth = width;
            int canvasHeight = height;

            if (maintainAspectRatio)
            {
                CalculateImageAspectRatio(source, width, height, addPadding, out Rectangle imageRect, out Size canvasSize);
                canvasWidth = canvasSize.Width;
                canvasHeight = canvasSize.Height;
                //double ratioX = width / (double)source.Width;
                //double ratioY = height / (double)source.Height;
                //double ratio = ratioX < ratioY ? ratioX : ratioY;

                //if (!addPadding)
                //{
                //    canvasWidth = (int)(source.Height * ratio);
                //    canvasHeight = (int)(source.Width * ratio);
                //}
            }
            return new Size(canvasWidth, canvasHeight);
        }
    }
}
